﻿using System.Linq;
using System.Text;
using Roslyn.Compilers;
using Roslyn.Compilers.Compilation;
using Roslyn.Compilers.Common;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;
using Roslyn.Services.CSharp;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections;
using System.Collections.Generic;
using System;

namespace Analysis.Test
{
    [TestClass]
    public class BaseTest
    {

        private IEnumerable<ClassDeclarationSyntax> TestClassesSyntaxNodes { get; set; }
        private CommonCompilation TestClassesCompilation { get; set; }
        private IProject TestProject { get; set; }
        private ISemanticModel TestSemanticModel;

        [TestInitialize]
        public void Setup()
        {
            TestProject = Solution.LoadStandAloneProject(@"C:\Users\FredrikSkeel\Documents\Visual Studio 2012\Projects\TestabilityCodeIssues\Analysis.Test\Analysis.Test.csproj");

            TestClassesCompilation = TestProject.GetCompilation();

            TestClassesSyntaxNodes = TestClassesCompilation.SyntaxTrees.Select(tree => tree.GetRoot().DescendantNodes().Where(cn => cn.Kind == (int)SyntaxKind.ClassDeclaration))
                .SelectMany(node => node).Select(node => (ClassDeclarationSyntax)node).Where(c => c.Identifier.ValueText == "Dto1").ToList();

            TestSemanticModel = TestProject.Documents.Single(d => d.Name == "TestClasses.cs").GetSemanticModel();
        }

        protected ClassDeclarationSyntax GetSyntaxNodeForClass(Type c)
        {
            var identifier = GetIdentifier(c);
            return TestClassesSyntaxNodes.FirstOrDefault(n => n.Identifier.ValueText == identifier);
        }

        protected NamedTypeSymbol GetSymbolForClass(Type c)
        {
            var identifier = GetIdentifier(c);
            var node = GetSyntaxNodeForClass(c);

            return GetSymbol(node, identifier) as NamedTypeSymbol;
        }

        /// <summary>
        /// Gets the symbol of a node.
        /// </summary>
        /// <param name="node">The target node.</param>
        /// <param name="identifierName">An optional identifier name.</param>
        /// <returns>The associated symbol.</returns>
        protected ISymbol GetSymbol(SyntaxNode node, string identifierName = null)
        {
            ISymbol symbol = null;

            // find symbol of declaration
            symbol = TestSemanticModel.GetDeclaredSymbol(node) as Symbol;

            if (symbol == null)
            {
                // find symbol of an expression
                var symbolInfo = TestSemanticModel.GetSymbolInfo(node);
                symbol = symbolInfo.Symbol as Symbol;

                if (symbol == null)
                {
                    if (symbolInfo.CandidateSymbols.Count == 1)
                        symbol = symbolInfo.CandidateSymbols[0] as Symbol;

                    // attempt using symbol lookup
                    if (symbol == null && identifierName != null)
                        symbol = TestSemanticModel.LookupSymbols(node.Span.Start, name: identifierName).SingleOrDefault();
                }
            }

            return symbol;
        }

        protected string GetIdentifier(Type type)
        {
            return type.Name;
        }
    }
}
